import DownOutlined from '@ant-design/icons/DownOutlined';
import UpOutlined from '@ant-design/icons/UpOutlined';
import classNames from 'classnames';
import type { InputNumberProps as RcInputNumberProps } from 'rc-input-number';
import RcInputNumber from 'rc-input-number';
import * as React from 'react';
import { useContext } from 'react';

import { FormItemInputContext, NoFormStyle } from '@/YmForm/context';
import type { ValidateStatus } from '@/YmForm/interface/FormItemType';
import { NoCompactStyle, useCompactItemContext } from '@/YmSpace/Compact';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';

type SizeType = 'large' | 'middle' | 'small';

type ValueType = string | number;
export interface InputNumberProps<T extends ValueType = ValueType>
  extends Omit<
    RcInputNumberProps<T>,
    'prefix' | 'size' | 'controls' | 'onChange'
  > {
  prefixCls?: string;
  addonBefore?: React.ReactNode;
  addonAfter?: React.ReactNode;
  prefix?: React.ReactNode;
  size?: SizeType;
  disabled?: boolean;
  bordered?: boolean;
  status?: InputStatus;
  controls?: boolean | { upIcon?: React.ReactNode; downIcon?: React.ReactNode };
  onChange?: (newValue: any) => void;
}

const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>(
  (props, ref) => {
    const [focused, setFocus] = React.useState(false);
    const inputRef = React.useRef<HTMLInputElement>(null);

    React.useImperativeHandle(ref, () => inputRef.current!);

    const {
      className,
      size: customizeSize,
      disabled: customDisabled,
      prefixCls = 'ym-input-number',
      addonBefore,
      addonAfter,
      prefix,
      bordered = true,
      readOnly,
      status: customStatus,
      controls,
      ...others
    } = props;

    const { compactSize, compactItemClassnames } = useCompactItemContext(
      prefixCls,
      'ltr',
    );
    let upIcon = <UpOutlined className={`${prefixCls}-handler-up-inner`} />;
    let downIcon = (
      <DownOutlined className={`${prefixCls}-handler-down-inner`} />
    );
    const controlsTemp = typeof controls === 'boolean' ? controls : undefined;

    if (typeof controls === 'object') {
      upIcon =
        typeof controls.upIcon === 'undefined' ? (
          upIcon
        ) : (
          <span className={`${prefixCls}-handler-up-inner`}>
            {controls.upIcon}
          </span>
        );
      downIcon =
        typeof controls.downIcon === 'undefined' ? (
          downIcon
        ) : (
          <span className={`${prefixCls}-handler-down-inner`}>
            {controls.downIcon}
          </span>
        );
    }

    const {
      hasFeedback,
      status: contextStatus,
      isFormItemInput,
      feedbackIcon,
    } = useContext(FormItemInputContext);
    const mergedStatus = getMergedStatus(contextStatus, customStatus) as
      | ValidateStatus
      | undefined;

    const mergeSize = compactSize ?? customizeSize;
    // ===================== Disabled =====================
    const mergedDisabled = customDisabled;

    const inputNumberClass = classNames(
      {
        [`${prefixCls}-lg`]: mergeSize === 'large',
        [`${prefixCls}-sm`]: mergeSize === 'small',
        [`${prefixCls}-borderless`]: !bordered,
        [`${prefixCls}-in-form-item`]: isFormItemInput,
      },
      getStatusClassNames(prefixCls, mergedStatus),
      compactItemClassnames,
      className,
    );

    let element = (
      <RcInputNumber
        ref={inputRef}
        disabled={mergedDisabled}
        className={inputNumberClass}
        upHandler={upIcon}
        downHandler={downIcon}
        prefixCls={prefixCls}
        readOnly={readOnly}
        controls={controlsTemp}
        {...others}
      />
    );

    if (prefix !== null || hasFeedback) {
      const affixWrapperCls = classNames(
        `${prefixCls}-affix-wrapper`,
        getStatusClassNames(
          `${prefixCls}-affix-wrapper`,
          mergedStatus,
          hasFeedback,
        ),
        {
          [`${prefixCls}-affix-wrapper-focused`]: focused,
          [`${prefixCls}-affix-wrapper-disabled`]: props.disabled,
          [`${prefixCls}-affix-wrapper-sm`]: customizeSize === 'small',
          [`${prefixCls}-affix-wrapper-lg`]: customizeSize === 'large',
          [`${prefixCls}-affix-wrapper-readonly`]: readOnly,
          [`${prefixCls}-affix-wrapper-borderless`]: !bordered,
          // className will go to addon wrapper
          [`${className}`]: !(addonBefore || addonAfter) && className,
        },
      );

      element = (
        <div
          className={affixWrapperCls}
          style={props.style}
          onMouseUp={() => inputRef.current!.focus()}
        >
          {prefix && <span className={`${prefixCls}-prefix`}>{prefix}</span>}
          {React.cloneElement(element, {
            style: null,
            value: props.value,
            onFocus: (event: React.FocusEvent<HTMLInputElement>) => {
              setFocus(true);
              props.onFocus?.(event);
            },
            onBlur: (event: React.FocusEvent<HTMLInputElement>) => {
              setFocus(false);
              props.onBlur?.(event);
            },
          })}
          {hasFeedback && (
            <span className={`${prefixCls}-suffix`}>{feedbackIcon}</span>
          )}
        </div>
      );
    }

    if (addonBefore !== null || addonAfter !== null) {
      const wrapperClassName = `${prefixCls}-group`;
      const addonClassName = `${wrapperClassName}-addon`;
      const addonBeforeNode = addonBefore ? (
        <div className={addonClassName}>{addonBefore}</div>
      ) : null;
      const addonAfterNode = addonAfter ? (
        <div className={addonClassName}>{addonAfter}</div>
      ) : null;

      const mergedWrapperClassName = classNames(
        `${prefixCls}-wrapper`,
        wrapperClassName,
      );

      const mergedGroupClassName = classNames(
        `${prefixCls}-group-wrapper`,
        {
          [`${prefixCls}-group-wrapper-sm`]: customizeSize === 'small',
          [`${prefixCls}-group-wrapper-lg`]: customizeSize === 'large',
        },
        getStatusClassNames(
          `${prefixCls}-group-wrapper`,
          mergedStatus,
          hasFeedback,
        ),
        className,
      );
      element = (
        <div className={mergedGroupClassName} style={props.style}>
          <div className={mergedWrapperClassName}>
            {addonBeforeNode && (
              <NoCompactStyle>
                <NoFormStyle status override>
                  {addonBeforeNode}
                </NoFormStyle>
              </NoCompactStyle>
            )}
            {React.cloneElement(element, {
              style: null,
              disabled: mergedDisabled,
            })}
            {addonAfterNode && (
              <NoCompactStyle>
                <NoFormStyle status override>
                  {addonAfterNode}
                </NoFormStyle>
              </NoCompactStyle>
            )}
          </div>
        </div>
      );
    }

    return element;
  },
);

export default InputNumber as (<T extends ValueType = ValueType>(
  props: React.PropsWithChildren<InputNumberProps<T>> & {
    ref?: React.Ref<HTMLInputElement>;
  },
) => React.ReactElement) & { displayName?: string };
